/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
using System.Xml.XPath;
using DaveSexton.DocProject.Sandcastle.BuildComponents.TypeConverters.BuiltIn;
using System.Xml;

namespace DaveSexton.DocProject.Sandcastle.BuildComponents.Editors.BuiltIn
{
	internal sealed partial class SharedContentComponentEditorDialog : DaveSexton.DocProject.Sandcastle.BuildComponents.Editors.CancelableBaseEditorDialog
	{
		#region Public Properties
		#endregion

		#region Private / Protected
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SharedContentComponentEditorDialog" /> class.
		/// </summary>
		public SharedContentComponentEditorDialog()
		{
			InitializeComponent();
		}
		#endregion

		#region Methods
		[System.Diagnostics.DebuggerHidden]
		private static Exception IsValidXPathExpression(string xpath, XmlNamespaceManager manager)
		{
			try
			{
				XPathExpression.Compile(xpath, manager);
			}
			catch (ArgumentException ex) { return ex; }
			catch (XPathException ex) { return ex; }

			return null;
		}

		private static Uri GetNamespaceUri(DataRow row, out string prefix)
		{
			prefix = row["prefix"] as string ?? "";

			if ((prefix = prefix.Trim()).Length == 0)
			{
				prefix = null;
				return null;
			}

			Uri uri;
			Uri.TryCreate(row["name"] as string ?? "", UriKind.RelativeOrAbsolute, out uri);

			return uri;
		}

		protected override void UpdateForBuildComponent(SandcastleBuildComponent component)
		{
			XPathNavigator navigator = component.CreateNavigator();

			SuspendLayout();

			try
			{
				namespacesBindingSource.DataSource = CreateNamespacesDataTable(navigator);
				replacementsBindingSource.DataSource = CreateReplacementsDataTable(navigator);
				contentBindingSource.DataSource = CreateContentDataTable(navigator);
			}
			finally
			{
				ResumeLayout();
			}
		}

		private static DataTable CreateNamespacesDataTable(IXPathNavigable component)
		{
			DataTable table = new DataTable();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataColumn column = table.Columns.Add("prefix", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			column = table.Columns.Add("name", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			FillNamespacesDataTable(component, table);

			return table;
		}

		private static DataTable CreateReplacementsDataTable(IXPathNavigable component)
		{
			DataTable table = new DataTable();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataColumn column = table.Columns.Add("elements", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = "//include|//includeAttribute";

			column = table.Columns.Add("item", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = "string(@item)";

			column = table.Columns.Add("parameters", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = "parameter";

			column = table.Columns.Add("attribute", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = "string(@name)";

			FillReplacementsDataTable(component, table);

			return table;
		}

		private static DataTable CreateContentDataTable(IXPathNavigable component)
		{
			DataTable table = new DataTable();
			table.Locale = System.Globalization.CultureInfo.CurrentCulture;

			DataColumn column = table.Columns.Add("file", typeof(string));
			column.AllowDBNull = false;
			column.DefaultValue = string.Empty;

			FillContentDataTable(component, table);

			return table;
		}

		private static void FillNamespacesDataTable(IXPathNavigable component, DataTable table)
		{
			XPathNavigator navigator = component.CreateNavigator();

			foreach (XPathNavigator content in navigator.SelectChildren("context", ""))
			{
				DataRow row = table.NewRow();
				row["prefix"] = content.GetAttribute("prefix", "");
				row["name"] = content.GetAttribute("name", "");
				table.Rows.Add(row);
			}
		}

		private static void FillReplacementsDataTable(IXPathNavigable component, DataTable table)
		{
			XPathNavigator navigator = component.CreateNavigator();

			foreach (XPathNavigator content in navigator.SelectChildren("replace", ""))
			{
				DataRow row = table.NewRow();
				row["elements"] = content.GetAttribute("elements", "");
				row["item"] = content.GetAttribute("item", "");
				row["parameters"] = content.GetAttribute("parameters", "");
				row["attribute"] = content.GetAttribute("attribute", "");
				table.Rows.Add(row);
			}
		}

		private static void FillContentDataTable(IXPathNavigable component, DataTable table)
		{
			XPathNavigator navigator = component.CreateNavigator();

			foreach (XPathNavigator content in navigator.SelectChildren("content", ""))
			{
				string file = content.GetAttribute("file", "");

				if (!string.IsNullOrEmpty(file))
				{
					DataRow row = table.NewRow();
					row["file"] = file;
					table.Rows.Add(row);
				}
			}
		}

		protected override bool SaveChanges()
		{
			if (!ValidateData())
				return false;

			XPathNavigator navigator = BuildComponent.CreateNavigator();
			StringBuilder output = new StringBuilder(Math.Max(navigator.InnerXml.Length, 256));

			XmlWriterSettings settings = new XmlWriterSettings();
			settings.ConformanceLevel = ConformanceLevel.Fragment;
			settings.Indent = true;

			using (XmlWriter writer = XmlWriter.Create(output, settings))
			{
				DataTable table = namespacesBindingSource.DataSource as DataTable;

				if (table != null)
					WriteRowElements(writer, table, "context");

				table = replacementsBindingSource.DataSource as DataTable;

				if (table != null)
					WriteRowElements(writer, table, "replace");

				table = contentBindingSource.DataSource as DataTable;

				if (table != null)
					WriteRowElements(writer, table, "content");
			}

			navigator.InnerXml = output.ToString();

			return true;
		}

		private static void WriteRowElements(XmlWriter writer, DataTable table, string elementName)
		{
			foreach (DataRow row in table.Rows)
			{
				writer.WriteStartElement(elementName);

				foreach (DataColumn column in table.Columns)
					writer.WriteAttributeString(column.ColumnName, (string) row[column]);

				writer.WriteEndElement();
			}
		}

		private bool ValidateData()
		{
			foreach (DataGridViewRow row in namespacesDataGridView.Rows)
				foreach (DataGridViewCell cell in row.Cells)
					if (cell.ErrorText.Length > 0)
						return false;

			foreach (DataGridViewRow row in replacementsDataGridView.Rows)
				foreach (DataGridViewCell cell in row.Cells)
					if (cell.ErrorText.Length > 0)
						return false;

			foreach (DataGridViewRow row in contentDataGridView.Rows)
				foreach (DataGridViewCell cell in row.Cells)
					if (cell.ErrorText.Length > 0)
						return false;

			return true;
		}
		#endregion

		#region Event Handlers
		private void namespacesDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			e.Cancel = true;
		}

		private void replacementsDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			e.Cancel = true;
		}

		private void contentDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
		{
			e.Cancel = true;
		}

		private void namespacesDataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
		{
			if (e.RowIndex > -1)
			{
				DataGridViewRow gridRow = namespacesDataGridView.Rows[e.RowIndex];

				if (gridRow.IsNewRow)
					return;

				DataRowView row = (DataRowView) gridRow.DataBoundItem;

				string prefix = row["prefix"] as string ?? "";

				if (prefix.Trim().Length == 0)
					gridRow.Cells[prefixColumn.DisplayIndex].ErrorText = "Prefix is required.";
				else
					gridRow.Cells[prefixColumn.DisplayIndex].ErrorText = null;

				string name = row["name"] as string ?? "";

				if (name.Trim().Length == 0)
					gridRow.Cells[namespaceColumn.DisplayIndex].ErrorText = "Namespace URI is required.";
				else
					gridRow.Cells[namespaceColumn.DisplayIndex].ErrorText = null;
			}
		}

		private void replacementsDataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
		{
			if (e.RowIndex > -1)
			{
				DataGridViewRow gridRow = replacementsDataGridView.Rows[e.RowIndex];

				if (gridRow.IsNewRow)
					return;

				XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());

				foreach (DataRow namespaceRow in ((DataTable) namespacesBindingSource.DataSource).Rows)
				{
					string prefix;
					Uri namespaceUri = GetNamespaceUri(namespaceRow, out prefix);

					if (namespaceUri != null)
					{
						try
						{
							manager.AddNamespace(prefix, namespaceUri.ToString());
						}
						catch (ArgumentException) { }
					}
				}

				DataRowView row = (DataRowView) gridRow.DataBoundItem;

				Exception ex = null;

				string xpath = row.Row["elements"] as string ?? "";

				if (xpath.Trim().Length == 0 || (ex = IsValidXPathExpression(xpath, manager)) != null)
					gridRow.Cells[elementsColumn.DisplayIndex].ErrorText = (ex != null) ? ex.Message : "Invalid XPath expression.";
				else
					gridRow.Cells[elementsColumn.DisplayIndex].ErrorText = null;

				ex = null;
				xpath = row.Row["item"] as string ?? "";

				if (xpath.Trim().Length == 0 || (ex = IsValidXPathExpression(xpath, manager)) != null)
					gridRow.Cells[itemColumn.DisplayIndex].ErrorText = (ex != null) ? ex.Message : "Invalid XPath expression.";
				else
					gridRow.Cells[itemColumn.DisplayIndex].ErrorText = null;

				// parameters and attribute are optional
			}
		}

		private void contentDataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
		{
			if (e.RowIndex > -1)
			{
				DataGridViewRow gridRow = contentDataGridView.Rows[e.RowIndex];

				if (gridRow.IsNewRow)
					return;

				DataRowView row = (DataRowView) gridRow.DataBoundItem;

				string file = row["file"] as string ?? "";

				if (file.Trim().Length == 0)
					gridRow.Cells[fileColumn.DisplayIndex].ErrorText = "File is required.";
				else
					gridRow.Cells[fileColumn.DisplayIndex].ErrorText = null;
			}
		}
		#endregion
	}
}